%include 'boot.inc'
section loader vstart=LOADER_BASE_ADDR
	jmp .1
	align 4
	GDT_TABLE: dd 0x0
	           dd 0x0
	K_CODE_DES  Descriptor 0x00,0xFFFFF,G_4K|K_CODE_X
	K_DATA_DES  Descriptor 0x00,0xFFFFF,G_4K|K_DATA_RW
	K_VIDEO_DES Descriptor 0xb8000,0x7,G_4K|K_DATA_RW
	;below spcae is for udata ucode tss descriptor
	times 3 DQ 0
	GDT_LEN equ $-GDT_TABLE
	GDT_PTR  dw GDT_LEN-1
		 dd GDT_TABLE
	MEM_SIZE dd 0
	ARDES times 244 db 0
	ARDES_NUM dd 0
	
.1:
	;use bios query memory size
	xor ebx,ebx
	mov di,ARDES		
.go_on_query:
	mov eax,BIOS_ARDES_FUN
	mov ecx,20
	mov edx,SMAP
	int 0x15
	jc .end
	add di,20
	inc dword [ARDES_NUM]	
	cmp ebx,0
	jnz .go_on_query

	;find the max ARDES	
	mov ecx,[ARDES_NUM]
	mov di,ARDES
.find_max_size:
	mov edx,[di]
	mov eax,[di+8]
	add eax,edx
	cmp eax,ebx
	jb .f
	mov ebx,eax		
	add di,20
.f:	
	loop .find_max_size
	mov [MEM_SIZE],ebx

	;open addr 21
	cli
	in al,0x92
	or al,0x02
	out 0x92,al

	;load gdt table
	lgdt [GDT_PTR]	

	;go protect mode	
	xor eax,eax
	mov eax,cr0
	or  eax,0x1
	mov cr0,eax

	;flush instruction execute line	
	jmp dword K_CODE_SEL:.2

.2:
	[bits 32]
	;here we in protect mode
	mov ax,K_DATA_SEL
	mov ds,ax
	mov es,ax
	mov ss,ax
	mov ax,K_VIDEO_SEL
	mov gs,ax
	mov esp,LOADER_BASE_ADDR
.3:
	;load kernal and init
	mov ebx,KERNAL_LOAD_ADDR
	mov eax,KERNAL_LBA
	mov ecx,KERNAL_SECTOR
	call .rd_disk_32
	;now we should start paging
	call .setup_paging	
	;change GDT VGA base address	
	add dword [GDT_TABLE+K_VIDEO_SEL+4],0xc000_0000
	;change GDT ptr base address	
	add dword [GDT_PTR+2],0xc000_0000
	;set stack in kernal address
	add esp,0xc000_0000
	
	;set PD table address	
	mov eax,K_PD_BASE	
	mov cr3,eax
	
	;set PG flag
	mov eax,cr0
	or eax,0x8000_0000	
	mov cr0,eax

	;reload GDT PTR
	lgdt [GDT_PTR]
	;flush instruction line again
	jmp K_CODE_SEL:.flush
.flush:
	;kernal start address put in eax now
	call .init_kernal
	;now we jmp to kernal
	mov esp,KERNAL_STACK_ADDR
	jmp eax
.end:
	jmp $
.rd_disk_32:
	;set sector count
	mov ebp,esp
	push ecx
	push eax

	mov edx,ATA0_CBR_M_SCTOR_COUNT
	mov eax,ecx
	out dx,al
	
	;set LBA LOW
	mov edx,ATA0_CBR_M_LBA_LOW
	mov eax,[bp-8]
	out dx,al
	;set LBA MID
	mov edx,ATA0_CBR_M_LBA_MID
	mov ecx,8
	shr eax,cl
	out dx,al
	;set LBA HI
	mov edx,ATA0_CBR_M_LBA_HI
	mov ecx,16
	mov eax,[bp-8]
	shr eax,cl
	out dx,al
	
	;set LBA Last
	mov edx,ATA0_CBR_M_DEV
	mov eax,[bp-8]
	mov ecx,24
	shr ax,cl
	and eax,0x0f
	out dx,al
		
	;set device port set master device use LBA MODE
	xor eax,eax
	or  eax,0xE0	
	out dx,al
	
	;tell ATA0 master channle read now
	mov edx,ATA0_CBR_M_COMMAND
	mov al,CBR_COMMAND_READ
	out dx,al
	
.not_ready:
	nop
	nop
	mov edx,ATA0_CBR_M_STATUS
	in al,dx	
	and al,0x08
	cmp al,0x08
	jnz .not_ready
		
	;sector count to ax	
	mov eax,[bp-4]
	mov edx,256
	mul edx
	mov ecx,eax
	
	mov edx,ATA0_CBR_M_DATA
.go_on_read:
	in ax,dx
	mov [ebx],ax
	add ebx,2
	loop .go_on_read

	pop eax
	pop ecx
	
	ret
.setup_paging:
	mov ebx,K_PD_BASE
	mov ecx,1024
	xor eax,eax
	xor esi,esi
.clear:
	mov dword [ebx+esi],eax
	add esi,4
	loop .clear
	
	mov eax,K_PT_BASE
	or eax,0x07
	;we make lower 1M address as they are
	mov dword [ebx],eax
	;we make our kernal in 0xc000_0000~0xc001_0000 map to lower 1M adress too
	mov dword [ebx+0xc00],eax
	;we make the last PD entry point PD it self so we can change PD PT entry by virtal address
	mov dword [ebx+0xffc],K_PD_BASE|0x7
	
	;now we map lower 1M space in PT table
	mov ebx,K_PT_BASE
	xor esi,esi
	xor eax,eax
	or eax,0x07
	mov ecx,256
.set_pt_entry:	
	mov dword [ebx+esi],eax
	add eax,0x1000
	add esi,4
	loop .set_pt_entry
		
	;last we share kernal left address
	mov ebx,K_PD_BASE
	mov ecx,254
	xor eax,eax
	mov eax,K_PT_BASE+0x1000
	or eax,0x07
	mov esi,769
.set_share:
	mov dword [ebx+esi*4],eax
	add eax,0x1000
	add esi,1
	loop .set_share
	ret

.init_kernal:
	mov ebx,KERNAL_LOAD_ADDR	
	movzx ecx,word [ebx+ELF32_PHNUM]
	xor eax,eax
	add eax,ebx
	add eax,[ebx+ELF32_PHOFF]
.parse_kernal:
	cmp dword [eax],PT_NULL
	jz .next
	cmp dword [eax],PT_GNU_STACK
	jz .next
	;len 
	mov edx,[eax+PHDR_FILESZ]
	push edx
	;dest
	mov edx,[eax+PHDR_VADDR]
	push edx
	;src
	mov edx,[eax+PHDR_OFF]	
	add edx,KERNAL_LOAD_ADDR
	push edx
	call .mem_copy
	add esp,12
.next:
	movzx edx,word [ebx+ELF32_PHENT_SIZE]	
	add eax,edx
	loop .parse_kernal
	mov eax,[ebx+ELF32_ENTRY]
	ret
	

;mem_copy(src,dest,len)
.mem_copy: 
	push ebp
	mov ebp,esp
	push ecx
	mov ecx,[ebp+16]
	mov edi,[ebp+12]
	mov esi,[ebp+8]	
	cld
	rep movsb
	pop ecx
	pop ebp	
	ret
